// Copyright (c) Microsoft Corporation.  All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.CSharp;
using System.Collections;
using System.Expressions;
using Microsoft.VisualStudio.DebuggerVisualizers;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;
using System.Query;

static class ExpressionTreeExtention
{
    static string ExtractName(string name)
    {
        int i = name.LastIndexOf("`");
        if (i > 0)
            name = name.Substring(0, i);
        return name;
    }
    static string ExtractGenericArguments(Type[] names)
    {
        StringBuilder builder = new StringBuilder("<");
        foreach (Type genericArgument in names)
        {
            if (builder.Length != 1) builder.Append(", ");
            builder.Append(ObtainOriginalName(genericArgument));
        }
        builder.Append(">");
        return builder.ToString();
    }
    public static string ObtainOriginalName(this Type type)
    {
        if (!type.IsGenericType)
        {
            return type.Name;
        }
        else
        {
            return ExtractName(type.Name) + ExtractGenericArguments(type.GetGenericArguments());
        }
    }
    public static string ObtainOriginalMethodName(this MethodInfo method)
    {
        if (!method.IsGenericMethod)
        {
            return method.Name;
        }
        else
        {
            return ExtractName(method.Name) + ExtractGenericArguments(method.GetGenericArguments());
        }
    }
    public static IEnumerable<PropertyInfo> GetPropertyInfos(this Type type)
    {
        PropertyInfo[] propertyInfos = null;
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Expression<>))
        {
            propertyInfos = type.BaseType.GetProperties(BindingFlags.Public |  BindingFlags.Instance);
        }
        else
        {
            propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        }
    
        foreach (PropertyInfo propertyInfo in propertyInfos)
        {
                yield return propertyInfo;
        }
    }
    public static void Do<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        foreach (T item in enumerable)
        {
            action(item);
        }
    }
    public static void Do(this IEnumerable enumerable, Action<Object> action)
    {
        foreach (object item in enumerable)
        {
            action(item);
        }
    }
    public static IEnumerable<Expression> ExpressionSubList(this ICollection input)
    {
            foreach (object tree in (IEnumerable)input)
            {
                if (tree is Expression)
                {
                    yield return tree as Expression;
                }
                else if (tree is MemberAssignment)
                {
                    yield return ((MemberAssignment)tree).Expression;
                }
            }
    }
}


[Serializable]
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ExpressionTreeRootContainerNode : TreeNode, ICustomTypeDescriptor 
{
    public ExpressionTreeRootContainerNode (SerializationInfo info, StreamingContext context) : base(info, context) { }
    protected override void Serialize(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { base.Serialize(si, context); }
    public ExpressionTreeRootContainerNode(Object value)
    {
        Text = "root";
        Expression[] avalue;
        
        if (value is Expression)
            avalue = new Expression[] { value as Expression}; 
        else if (value is Expression[])
            avalue = value as Expression[];
        else 
            return;
        
        foreach (Expression expression in avalue)
        {
            Nodes.Add(new ExpressionTreeNode (expression));
        }
    }
    #region ICustomTypeDescriptor Members

    #region uninteresting ICustomTypeDescriptor Members
    public String GetClassName()
    {
        return TypeDescriptor.GetClassName(this,true);
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this,true);
    }

    public String GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }
    public EventDescriptor GetDefaultEvent() 
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty() 
    {
        return TypeDescriptor.GetDefaultProperty(this, true);
    }

    public object GetEditor(Type editorBaseType) 
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes) 
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public object GetPropertyOwner(PropertyDescriptor pd) 
    {
        return this;
    }
    
    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    #endregion
    public PropertyDescriptorCollection GetProperties() 
    {
        PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
        Nodes.Do( s => {pds.Add(new ExpressionNodePropertyDescriptor(s as ExpressionTreeNode));});
            
        return pds;
    }
    #endregion
}


[Serializable]
[TypeConverter(typeof(ExpressionTreeNodeConverter ))]
public class ExpressionTreeNode : TreeNode, ICustomTypeDescriptor, ISerializable
{
    public ExpressionTreeNode(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        _type = (string)info.GetValue("type", typeof(string));
    }
    private string _type = "";

    public string Type
    {
        get { return _type; }
        set { _type = value; }
    }

    protected override void Serialize(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context)
    {
        si.AddValue("type", _type, _type.GetType());
        base.Serialize(si, context);
    }

    public ExpressionTreeNode(object value)
    {
        //_type = value.GetType().ObtainOriginalName();
        Text = value.ToString(); //.ObtainOriginalName();
        if (value is Expression)
        {
            ImageIndex = 2;
            SelectedImageIndex = 2;
              Tag = value;
            
            if (value is MethodCallExpression)
                _type += (value as MethodCallExpression).Method.ReturnType.ObtainOriginalName();
            if (value is ConstantExpression)
                _type += (value as ConstantExpression).Value.GetType().ObtainOriginalName();
            if (value is MemberExpression)
                _type += (value as MemberExpression).Type.ObtainOriginalName();
            if (value.GetType().IsGenericType && value.GetType().GetGenericTypeDefinition() == typeof(Expression<>))
                _type += (value.GetType().GetGenericArguments())[0].ObtainOriginalName();
            if (value is ParameterExpression)
                _type += (value as ParameterExpression).Type.ObtainOriginalName();

            

            foreach (PropertyInfo propertyInfo in GetPropertyInfosFiltered(value.GetType()))
            {
                Nodes.Add(new AttributeNode(value, propertyInfo));
            }
        }
        else
        {
            ImageIndex = 4;
            SelectedImageIndex = 4;
            Text = "\"" + value.ToString() + "\"";
        }
    }

    private static IEnumerable<PropertyInfo> GetPropertyInfosFiltered(Type type)
    {
        string[] ignoredproperties = {"nodetype", "object", "type", "name", "parameters"};
        var ts = type.GetPropertyInfos().Where(s => !ignoredproperties.Contains(s.Name.ToLower()));
        foreach (var t in ts)
        {
            yield return t;
        }
        
    }

    #region ICustomTypeDescriptor Members

    #region uninteresting ICustomTypeDescriptor Members
    public String GetClassName()
    {
        return TypeDescriptor.GetClassName(this,true);
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this,true);
    }

    public String GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }
    public EventDescriptor GetDefaultEvent() 
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty() 
    {
        return TypeDescriptor.GetDefaultProperty(this, true);
    }

    public object GetEditor(Type editorBaseType) 
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes) 
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public object GetPropertyOwner(PropertyDescriptor pd) 
    {
        return this;
    }
    
    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    #endregion
    public PropertyDescriptorCollection GetProperties() 
    {
        PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);

        foreach (object node in this.Nodes)
            pds.Add(new AttributeNodePropertyDescriptor(node as AttributeNode));

        return pds;
    }
    #endregion

}
internal class ExpressionTreeNodeConverter : ExpandableObjectConverter
{
    public override object ConvertTo(ITypeDescriptorContext context, 
                             System.Globalization.CultureInfo culture, 
                             object value, Type destType )
    {
        if( destType == typeof(string) && value is ExpressionTreeNode)
        {
            return (value as ExpressionTreeNode).Type;
        }
        return base.ConvertTo(context,culture,value,destType);
    }
}


[Serializable]
[TypeConverter(typeof(AttributeNodeConverter ))]
public class AttributeNode : TreeNode, ICustomTypeDescriptor
{
    public AttributeNode(SerializationInfo info, StreamingContext context) : base(info, context) { }
    public AttributeNode(Object attribute, PropertyInfo propertyInfo)
    {
        ImageIndex = 3;
        SelectedImageIndex = 3;

        Object value = propertyInfo.GetValue(attribute,null);
        if (value != null)
        {
            if (value.GetType().IsGenericType && value.GetType().GetGenericTypeDefinition() == typeof(ReadOnlyCollection<>))
            {
                var colValue = value as ICollection;
                Text = propertyInfo.Name + " : " + propertyInfo.PropertyType.ObtainOriginalName();

                if (colValue.ExpressionSubList().Count() == 0)
                    Text += " : Empty";
                colValue.ExpressionSubList().Do( s => { Nodes.Add(new ExpressionTreeNode(s)); } );
            }
            else if (value is Expression)
            {
                Text = propertyInfo.Name + " : " + propertyInfo.PropertyType.ObtainOriginalName();
                Text += ((Expression)value).NodeType;
                Nodes.Add(new ExpressionTreeNode(value));
            }
            else if (value is MethodInfo)
            {
                MethodInfo minfo = value as MethodInfo;
                Text += minfo.ObtainOriginalMethodName();
                IEnumerable<PropertyInfo> pi2 = attribute.GetType().GetPropertyInfos();
                PropertyInfo pi = pi2.First(s => s.Name == "Parameters");
                var value2 = (ICollection)pi.GetValue(attribute,null);
                value2.ExpressionSubList().Do( s => { Nodes.Add(new ExpressionTreeNode(s)); } );
            }
            else if (value is Type)
            {
                Text = propertyInfo.Name + " : " + propertyInfo.PropertyType.ObtainOriginalName();
                Type minfo = value as Type;
                Text += " : \"" + minfo.ObtainOriginalName() + "\"";
            }
            else
            {
                Text = propertyInfo.Name + " : " + propertyInfo.PropertyType.ObtainOriginalName();
                Text += " : \"" + value.ToString() + "\"";
            }
        }
        else
        {
            Text += " : null";
        }
    }
    protected override void Serialize(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context)
    {
        base.Serialize(si, context);
    }


    #region ICustomTypeDescriptor Members

    #region uninteresting ICustomTypeDescriptor Members
    public String GetClassName()
    {
        return TypeDescriptor.GetClassName(this,true);
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this,true);
    }

    public String GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }
    public EventDescriptor GetDefaultEvent() 
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty() 
    {
        return TypeDescriptor.GetDefaultProperty(this, true);
    }

    public object GetEditor(Type editorBaseType) 
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes) 
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public object GetPropertyOwner(PropertyDescriptor pd) 
    {
        return this;
    }
    
    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    #endregion
    public PropertyDescriptorCollection GetProperties() 
    {
        PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
        int i = 0;

        foreach (object node in this.Nodes)
        {
            if (node is AttributeNode)
                pds.Add(new AttributeNodePropertyDescriptor(node as AttributeNode));
            else if (node is ExpressionTreeNode)
                pds.Add(new ExpressionNodePropertyDescriptor(node as ExpressionTreeNode));
            else 
                pds.Add(new NodePropertyDescriptor(node as TreeNode));
        }
        return pds;
    }
    #endregion
}
internal class AttributeNodeConverter : ExpandableObjectConverter
{
    public override object ConvertTo(ITypeDescriptorContext context, 
                             System.Globalization.CultureInfo culture, 
                             object value, Type destType )
    {
        return "";
    }
}


public class AttributeNodePropertyDescriptor : PropertyDescriptor
{    
    private AttributeNode _treenode = null;

    public AttributeNodePropertyDescriptor(AttributeNode treenode) : base(treenode.ToString(), null )
    {
        this._treenode = treenode;
    } 

    public override AttributeCollection Attributes
    {
        get 
        { 
            return new AttributeCollection(null);
        }
    }
    public override bool CanResetValue(object component)
    {
        return true;
    }
    public override Type ComponentType
    {
        get 
        { 
            return this._treenode.GetType();
        }
    }
    public override string DisplayName
    {
        get 
        {
            return  this._treenode.Text;
        }
    }
    public override string Description
    {
        get
        {
            return  this._treenode.Text;
        }
    }
    public override object GetValue(object component)
    {
        return this._treenode;
    }
    public override bool IsReadOnly
    {
        get { return true;  }
    }
    public override string Name
    {
        get { return this._treenode.ToString(); }
    }
    public override Type PropertyType
    {
        get { return this._treenode.GetType(); }
    }
    public override void ResetValue(object component) {}
    public override bool ShouldSerializeValue(object component)
    {
        return true;
    }
    public override void SetValue(object component, object value)
    {
        // this.collection[index] = value;
    }
}    
public class ExpressionNodePropertyDescriptor : PropertyDescriptor
{    
    private ExpressionTreeNode _treenode = null;

    public ExpressionNodePropertyDescriptor(ExpressionTreeNode treenode) : base(treenode.ToString(), null )
    {
        this._treenode = treenode;
    } 

    public override AttributeCollection Attributes
    {
        get 
        { 
            return new AttributeCollection(null);
        }
    }
    public override bool CanResetValue(object component)
    {
        return true;
    }
    public override Type ComponentType
    {
        get 
        { 
            return this._treenode.GetType();
        }
    }
    public override string DisplayName
    {
        get 
        {
            return  this._treenode.Text;
        }
    }
    public override string Description
    {
        get
        {
            return  this._treenode.Text;
        }
    }
    public override object GetValue(object component)
    {
        return this._treenode;
    }
    public override bool IsReadOnly
    {
        get { return true;  }
    }
    public override string Name
    {
        get { return this._treenode.ToString(); }
    }
    public override Type PropertyType
    {
        get { return this._treenode.GetType(); }
    }
    public override void ResetValue(object component) {}
    public override bool ShouldSerializeValue(object component)
    {
        return true;
    }
    public override void SetValue(object component, object value)
    {
        // this.collection[index] = value;
    }
}    
public class NodePropertyDescriptor : PropertyDescriptor
{    
    private TreeNode _treenode = null;

    public NodePropertyDescriptor(TreeNode treenode) : base(treenode.ToString(), null )
    {
        this._treenode = treenode;
    } 

    public override AttributeCollection Attributes
    {
        get 
        { 
            return new AttributeCollection(null);
        }
    }
    public override bool CanResetValue(object component)
    {
        return false;
    }
    public override Type ComponentType
    {
        get 
        { 
            return this._treenode.GetType();
        }
    }
    public override string DisplayName
    {
        get 
        {
            return this._treenode.Text;
        }
    }
    public override string Description
    {
        get
        {
            return "display";// this._treenode.Text;
        }
    }
    public override object GetValue(object component)
    {
        return this._treenode;
    }
    public override bool IsReadOnly
    {
        get { return true;  }
    }
    public override string Name
    {
        get { return "display2"; } // this._treenode.ToString(); }
    }
    public override Type PropertyType
    {
        get { return this._treenode.GetType(); }
    }
    public override void ResetValue(object component) {}
    public override bool ShouldSerializeValue(object component)
    {
        return true;
    }
    public override void SetValue(object component, object value)
    {
        // this.collection[index] = value;
    }
}    
